#ifndef ORG_WXW0303_COMMON_FILEPATH_UTIL_H_
#define ORG_WXW0303_COMMON_FILEPATH_UTIL_H_

#include <linux/limits.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>

#include <functional>

#include "commonexception.h"
#include "bytearray.h"
#include "cxx-glibc-wrap.h"
#include "scopeguard.h"
#include "onexception.h"

/* RawPath,类似 RawByteArray.
 * o 不变量1(!): RawPath 内部保存的路径不会存放多余的'/'(包括末尾'/').
 *   * 类内部并不会维护这一不变量,不过类可以保证的是当提供给 assign(),构造函数(),append()
 *     时所使用的 src,size 不带有多余的'/'(以及末尾'/').那么不变量1成立.
 * o RawPath 与 RawByteArray 共有接口保持相同的语义.
 */
struct RawPath {

    /* 构建 RawPath 对象,指定其所使用的缓冲区,以及其初始值.
     * o 若初始值为空,则等同于根目录.
     * o src,size;若 src 为 nullptr 或者 size == 0,则表明其为空.
     */
    RawPath(void *buf,size_t capacity);
    RawPath(void *buf,size_t capacity,const char *src,size_t size);
    RawPath(void *buf,size_t capacity,const char *src);

    /* 将 src,size 确定的缓冲区指定为当前 RawPath 的值.
     * o 若 src,size 为空,则等同于根目录.
     * o src,size;若 src 为 nullptr 或者 size == 0,则表明其为空.
     */
    RawPath& assign(const char *src,size_t size);
    RawPath& assign(const char *src);

    /* 在需要的时候,会首先追加'/',再追加 src,size.
     * o 若 src,size 为空,则不执行任何操作.
     * o src,size;若 src 为 nullptr 或者 size == 0,则表明其为空.
     */
    RawPath& append(const char *src,size_t size);
    RawPath& append(const char *src);
    // 在需要的时候,在末尾追加一个'/'.
    RawPath& appendSlash();

    void followDotDot();

    template <typename F>
    void traverse(F &&f) const;
    bool isAbsolute() const;
    size_t capacity() const;
    RawPath& setToRoot();
    bool isRootDir() const;
    char* data();
    const char* data() const;
    const char* constData() const;
    char* rawData();
    const char* rawData() const;
    const char* constRawData() const;
    size_t size() const;
    void reserve(size_t size);
    // 当 size 为0时,表示根目录.
    void resize(size_t size);

    bool extensionIs(const char *name,size_t size) const;
    bool extensionIs(const char *name) const;
    // 移除所有尾部'/'.
    RawPath& removeTrailSlash();

    const char* isIn(const char *path,size_t size) const;
    const char* isIn(const char *path) const;
    const char* isIn(const RawPath &path) const;

    /* 测试当前路径中是否包含子串 src,size.并返回在当前路径中第一次出现的地址.
     * @param src,size 若为空串,则此时一定包含,并且返回 constRawData().
     * @return nullptr 当前路径中不包含子串 src,size.
     * @note 返回值指向的缓冲区不确保以'\0'结尾!!!
     */
    const char* contains(const char *src,size_t size) const;
    const char* contains(const char *src) const;
private:
    RawByteArray buffer_ ;
};

struct PathUtil {
    /* 返回在 src,size 中第一个尾部'/'的地址.如: src == 'hello///',则返回 &src[5].
     * @return RETURN [src,RETURN) 确定了不带尾部'/'的路径.
     */
    static const char* getTrailSlashPtr(const char *src,size_t size);
    static const char* getTrailSlashPtr(const char *src);

    /* 若 src 是一个 component,不包含路径分隔符;则返回真.
     * 空串不被认为是一个 component.
     */
    static bool isOneComponent(const char *src,size_t size);
    static bool isOneComponent(const char *src);

    /* 测试 s,ssize 是否在目录 p,psize 中.
     * @param p,psize,s,ssize 必须是 realpath.函数内不会检测(!!!).
     * @return nullptr 表明 s,ssize 不在 p,psize 下面.
     * @return 其他值(RETURN) 表明 s,ssize 在 p,psize 下面,而且 [RETURN,s + ssize)
     *  确定了 s 之中除 p 之外的剩余部分.
     */
    static const char* isIn(const char *p,size_t psize,const char *s,size_t ssize);
    static const char* isIn(const char *p,size_t psize,const char *s);
    static const char* isIn(const char *p,const char *s);
    static const char* isIn(const char *p,const char *s,size_t ssize);

    /* 测试文件 filename 的扩展名是否是 name.
     * @param filename,filenamesize 使用其最后一个 component 来匹配.若其没有最后一个 componnet
     *  (如:'/','/home/'),则很显然返回 false.
     * @param name,size 不应该以'.'开头;若以'.'开头,则其会被认为是扩展名的一部分.
     *  如若想测试文件的扩展名是否是"txt",则 name == 'txt',若 name 为'.txt',则文件名得是
     *  'hello..txt'才能匹配.
     */
    static bool extensionIs(const char *filename,size_t filenamesize,const char *name,size_t size);

    /* 返回 path 中最后一个 component 的地址.
     * @return nullptr 则表明 path 中不存在最后一个 component,如: path='/','/hoem/'.
     * @return 其他值 很显然,此时 [返回值,path+size) 就是最后一个 component.
     */
    static const char* lastComponent(const char *path,size_t size);

    /* 路径 component 遍历,即对于路径中的每一个非空 component,都调用一次 f().
     * o f 的调用形式:
     *   PathTraverseReturn f(const char *component_ptr,size_t component_size,
     *                        ComponentLocation location);
     *   * f() 可以通过其返回来控制遍历的过程.当前支持以下返回值:
     *     a BREAK;此时打破遍历过程.
     *     b CONTINUE;继续遍历.
     * o f() 不能更改 path,size 确定的缓冲区.
     */
    enum PathTraverseReturn {
        BREAK = 0,
        CONTINUE
    };
    enum ComponentLocation {
        LAST = 0,
        MIDDLE,
        LAST_BUT_WITH_TRAIL_SLASH
    };
    template <typename F>
    static void traverse(const char *path,size_t size,F &&f);

    static void traverse2(const char *path,size_t size,std::function<void(const char*,size_t)> f);

    /* 读取符号链接文件 path 的内容,存放到 dst,isize 确定的缓冲区中,不会追加'\0'.
     * @return 返回写入到 dst 中的字节数,若内存不足,则抛出异常.
     */
    static size_t readlink(char *dst,size_t isize,const char *path);
    template <typename ByteArrayT>
    static void readlink(ByteArrayT &bytes,const char *path);

    /* 判断 path 是否是绝对路径.
     * 若 path,size 为空,则不被认为是绝对路径.
     */
    static bool isAbsolute(const char *path) noexcept;
    static bool isAbsolute(const char *path,size_t size) noexcept;

    /* 解析 ptr,size 确定的路径;移除多余的符号链接,'.','..','/'.
     * o 最后一个 component 的定义:
     * o 若 ptr,size 指定的路径带有末尾'/',则仅当最后一个 component 不存在,或者不是目录时
     *   才会保留'/'.
     * @param ptr,size ptr,size 确定的路径必须是绝对路径
     * @param dst,max_size 解析后的路径存放在这里,并不会自动追加'\0'.
     * @param fl 若最后一个 component 是符号链接,则指定是否追随该符号链接.
     *  o FOLLOW_LINK;追随符号链接.
     *  o DONT_FOLLOW_LINK;不追随符号链接.
     * @param ce 指定了对解析过程中遇到的 component 存在性要求:
     *  o ALL_EXISTS;指定了遇到的所有 component 必须存在.
     *  o ALL_EXISTS_BUT_LAST;指定了除最后一个 component 之外,其他必须存在.
     *  o ALL_NEED_NOT_EXISTS;遇到的 component 可以存在,可以不存在.
     * @return 若 dst 指向的缓冲区足够,则返回写入到 dst 中的字节数;否则抛出异常.
     * @note 当抛出异常时,dst 指向的缓冲区其内容已经被改变,由于性能原因,函数内并没有恢复.
     */
    enum FollowLinkVal {
        FOLLOW_LINK = 0,
        DONT_FOLLOW_LINK
    };
    enum ComponentExistsVal{
        ALL_EXISTS = 0,
        ALL_EXISTS_BUT_LAST,
        ALL_NEED_NOT_EXISTS
    };
    static size_t advancedRealpath(char *dst,size_t max_size,
                           const char *ptr,size_t size,
                           FollowLinkVal fl = FOLLOW_LINK,
                           ComponentExistsVal ce = ALL_NEED_NOT_EXISTS);
};

/* 遍历目录,对于目录中每一个 entry 调用遍历函数 f.
 * @param dirname 不会检查其合法性!!!
 * @param f 其调用形式:
 *  DirTravseEnum f(const struct dirent &p);
 *  o p;指向着 entry;此时进程的当前工作目录已经是 p 所处的目录,所以可以对 p 直接处理.
 *  o return;BREAK,CONTINUE;
 */
enum class DirTraverseEnum {
    RECURSIVE = 0,
    DONTRECURSIVE,

    BREAK,
    CONTINUE
};
template <typename F>
void dirTraverse(const char *dirname,DirTraverseEnum recursiveArg,F &&f)
{
    bool recursive = (recursiveArg == DirTraverseEnum::RECURSIVE);

    int cwdfd = cxx_open(".",O_RDONLY);
    ON_SCOPE_EXIT {
        cxx_fchdir(cwdfd);
        cxx_close(cwdfd);
    };

    DIR *dirstream = cxx_opendir(dirname);
    ON_SCOPE_EXIT {
        cxx_closedir(dirstream);
    };

    cxx_fchdir(cxx_dirfd(dirstream));
    struct dirent entry;
    while (cxx_readdir_r(dirstream,&entry)) {
        if (f(entry) == DirTraverseEnum::BREAK)
            break;

        if (recursive && entry.d_type == DT_DIR &&
            strcmp(entry.d_name,".") != 0 && strcmp(entry.d_name,"..") != 0)
        {
            dirTraverse(entry.d_name,recursiveArg,std::forward<F>(f));
        }
    }

    return ;
}

////////////////////////////PathUtil Implement Begin////////////////////////////

inline const char* PathUtil::getTrailSlashPtr(const char *src)
{
    return getTrailSlashPtr(src,safe_strlen(src));
}

inline const char* PathUtil::isIn(const char *p,size_t psize,const char *s)
{
    return isIn(p,psize,s,strlen(s));
}

inline const char* PathUtil::isIn(const char *p,const char *s)
{
    return isIn(p,strlen(p),s,strlen(s));
}

inline const char* PathUtil::isIn(const char *p,const char *s,size_t ssize)
{
    return isIn(p,strlen(p),s,ssize);
}

inline bool PathUtil::isOneComponent(const char *src,size_t size)
{
    return src != nullptr && size != 0 && memchr(src,'/',size) == nullptr;
}

inline bool PathUtil::isOneComponent(const char *src)
{
    return isOneComponent(src,safe_strlen(src));
}


inline bool
PathUtil::isAbsolute(const char *path) noexcept
{
    return path != nullptr && path[0] == '/';
}

inline bool
PathUtil::isAbsolute(const char *path,size_t size) noexcept
{
    return path != nullptr && size != 0 && path[0] == '/';
}

template <typename ByteArrayT>
void PathUtil::readlink(ByteArrayT &bytes,const char *path)
{
    constexpr size_t PATH_MAX_1 = PATH_MAX - 1;
    size_t old_size = bytes.size();
    bytes.resize(old_size + PATH_MAX_1);
    size_t readlink_ret = readlink(bytes.rawData() + old_size,PATH_MAX_1,path);
    bytes.resize(old_size + readlink_ret);
    return ;
}

template <typename F>
void PathUtil::traverse(const char *path,size_t size,F &&f)
{
    if (path == nullptr && size != 0)
        THROW(EINVAL);
    const char * const path_end = path + size;

    //  循环不变量1: start 总是指向着 path_end 或者指向着第一个非'/'字符.
    const char *start = path;
    while (start < path_end && *start == '/')
        ++start;
    for (const char *end = path,*end_bak = path; ; start = end) {
        if (start >= path_end)
            break;
        for (end = start; end < path_end && *end != '/'; ++end)
            /*  */;
        end_bak = end;
        while (end < path_end && *end == '/')
            ++end;
        // 到执行流到这里时,path,size 一定不为空,那么 end[-1] 就总是合法的!
        auto ret = f(start,end_bak - start,
                     end >= path_end ?
                         (end[-1] == '/' ? LAST_BUT_WITH_TRAIL_SLASH : LAST) :
                         MIDDLE);
        if (ret == PathTraverseReturn::BREAK)
            break;
    }
    return ;
}

inline void PathUtil::traverse2(const char *path,size_t size,std::function<void(const char*,size_t)> f)
{
    PathUtil::traverse(path,size,[&](const char *p,size_t s,ComponentLocation l) ->
        PathTraverseReturn
    {
        f(p,s);
        return CONTINUE;
    });
    return ;
}

////////////////////////////PathUtil Implement End//////////////////////////////

////////////////////////////RawPath Implement Begin/////////////////////////////
/* 实现须知:
 * o 不变量.1: buffer_.isEmpty() 始终不成立.
 */

inline const char* RawPath::contains(const char *src,size_t size) const
{
    return (src == nullptr || size == 0) ? buffer_.constRawData() :
        static_cast<const char*>(memmem(buffer_.constRawData(),buffer_.size(),src,size));
}

inline void RawPath::reserve(size_t size)
{
    buffer_.reserve(size);
    return ;
}

inline bool RawPath::extensionIs(const char *name,size_t size) const
{
    return PathUtil::extensionIs(buffer_.constRawData(),buffer_.size(),name,size);
}

inline bool RawPath::extensionIs(const char *name) const
{
    return extensionIs(name,safe_strlen(name));
}

inline void RawPath::resize(size_t size)
{
    if (size == 0)
        setToRoot();
    else
        buffer_.resize(size);
    return ;
}

inline RawPath::RawPath(void *buf,size_t capacity):
    buffer_(buf,capacity)
{
    buffer_.append('/');
}

inline RawPath::RawPath(void *buf,size_t capacity,const char *src,size_t size):
    buffer_(buf,capacity)
{
    assign(src,size);
}

inline RawPath::RawPath(void *buf,size_t capacity,const char *src):
    RawPath(buf,capacity,src,safe_strlen(src))
{
}

inline size_t RawPath::capacity() const
{
    return buffer_.capacity();
}

inline RawPath& RawPath::setToRoot()
{
    buffer_.resize(0);
    buffer_.append('/');
    return *this;
}

inline bool RawPath::isRootDir() const
{
    return buffer_.size() == 1 && buffer_.at_s(0) == '/';
}

inline const char* RawPath::constData() const
{
    return buffer_.constData();
}

inline const char* RawPath::constRawData() const
{
    return buffer_.constRawData();
}

inline char* RawPath::data()
{
    return buffer_.data();
}

inline char* RawPath::rawData()
{
    return buffer_.rawData();
}

inline RawPath& RawPath::assign(const char *src)
{
    assign(src,safe_strlen(src));
    return *this;
}

inline RawPath& RawPath::append(const char *src)
{
    return append(src,safe_strlen(src));
}

inline bool RawPath::isAbsolute() const
{
    return buffer_.at_s(0) == '/';
}

inline size_t RawPath::size() const
{
    return buffer_.size();
}

inline const char* RawPath::rawData() const
{
    return buffer_.constRawData();
}

inline const char* RawPath::data() const
{
    return buffer_.constData();
}

template <typename F>
void RawPath::traverse(F &&f) const
{
    // 完美转发.
    PathUtil::traverse(buffer_.constRawData(),buffer_.size(),std::forward<F>(f));
    return ;
}

inline RawPath& RawPath::appendSlash()
{
    if (buffer_.back_s() != '/')
        buffer_.append('/');
    return *this;
}

inline const char* RawPath::isIn(const char *path,size_t size) const
{
    return PathUtil::isIn(path,size,constRawData(),this->size());
}

inline const char* RawPath::isIn(const char *path) const
{
    return isIn(path,strlen(path));
}

inline const char* RawPath::isIn(const RawPath &path) const
{
    return isIn(path.constRawData(),path.size());
}

inline const char* RawPath::contains(const char *src) const
{
    return src == nullptr ? constRawData() : contains(src,strlen(src));
}

////////////////////////////RawPath Implement End///////////////////////////////

#endif // ORG_WXW0303_COMMON_FILEPATH_UTIL_H_
